#include <stdio.h>
#include <windows.h>

#include "jsonmain.h"
#include "webservicecall.h"
#include "webservice.h"
#include "lib.h"
#include "opts.h"
#include "log.h"

static const std::string base64_chars = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";

// CSN-8893 - POST data format used for Credit request to external Wallet
#define POSTDATA_CREDITWALLET "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
<transfer xmlns=\"http://integration.api/jaxb/model\">\
<externalId>%s</externalId>\
<amount currency=\"%s\" value=\"%d\"/>\
<references>\
<property key=\"refnum1\" value =\"%I64d\"/>\
<property key=\"description\" value =\"Poker - Table Cashout\"/>\
<property key=\"sessionId\" value =\"%I64d\"/>\
<property key=\"gameId\" value =\"%d\"/>\
<property key=\"machId\" value =\"%d\"/>\
<property key=\"roundId\" value =\"%I64d\"/>\
</references>\
<transactionId>%s</transactionId>\
<categoryCode>PK-CC-RNG</categoryCode>\
</transfer>"

// CSN-8893 - POST data format used for Debit request to external Wallet
#define POSTDATA_DEBITWALLET "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
<transfer xmlns=\"http://integration.api/jaxb/model\">\
<externalId>%s</externalId>\
<amount currency=\"%s\" value=\"%d\"/>\
<references>\
<property key=\"refnum1\" value =\"%I64d\"/>\
<property key=\"description\" value =\"Poker - Table Add Chips\"/>\
<property key=\"sessionId\" value =\"%I64d\"/>\
<property key=\"gameId\" value =\"%d\"/>\
<property key=\"machId\" value =\"%d\"/>\
<property key=\"roundId\" value =\"%I64d\"/>\
</references>\
<transactionId>%s</transactionId>\
<categoryCode>PK-PC-RNG</categoryCode>\
</transfer>"

#define RoundDiv(num, denom)	(((num) + ((denom) >> 1)) / (denom))

static WebService* wsAuthenticate = (WebService*)0;
static WebService* wsCreditWallet = (WebService*)0;
static WebService* wsDebitWallet = (WebService*)0;
static WebService* wsGetBalanceWallet = (WebService*)0;

WebServiceCall::WebServiceCall()
{
	// Header 1: credentials
	String sAuthHeader = ServerOpts::intAPIUser + String(":") + ServerOpts::intAPIPassword;
	sAuthHeader = base64_encode(sAuthHeader);
	sAuthHeader = String("Authorization: Basic ") + sAuthHeader;

	// Header 2: we want a JSON response
	String sHeaders = sAuthHeader + String("\r\nAccept: application/json");
	sHeaders = sHeaders + String("\r\nX-System-Id: CASINO");		// CSN-14216: new header (Integration API requirement)
	wstring wsHeaders = sHeaders;

	wstring sServer = ServerOpts::intAPIHost;

	// Authenticate web service
	wsAuthenticate = new WebService(sServer, false, L"POST", wsHeaders);
	
	// CSN-8893 - Initialize web services for External Wallet
	if (ServerOpts::bUseExternalWallet)
	{
		// Set HTTP headers
		// TODO: This is a temporary endpoint for CSN-8893, ServerOpts credentials should used instead
		String sWalletHeaders = String("Authorization: Basic cXVhZDpzZXJ2ZXI="
			                           "\r\nContent-Type: application/xml"
									   "\r\nAccept: application/xml");
		wstring wsWalletHeaders = sWalletHeaders;

		// Credit web service
		wsCreditWallet = new WebService(sServer, false, L"POST", wsWalletHeaders);

		// Debit web service
		wsDebitWallet = new WebService(sServer, false, L"POST", wsWalletHeaders);

		// Get Balance web service
		wsGetBalanceWallet = new WebService(sServer, false, L"POST", wsWalletHeaders);
	}
	else
	{
		wsCreditWallet = (WebService*)0;
		wsDebitWallet = (WebService*)0;
		wsGetBalanceWallet = (WebService*)0;
	}
}

WebServiceCall::~WebServiceCall()
{
	delete wsAuthenticate;
	delete wsCreditWallet;
	delete wsDebitWallet;
	delete wsGetBalanceWallet;

	wsAuthenticate = (WebService*)0;
	wsCreditWallet = (WebService*)0;
	wsDebitWallet = (WebService*)0;
	wsGetBalanceWallet = (WebService*)0;
}

bool WebServiceCall::Authenticate(String sToken, String& sPID, String& sClientType, String& sCurrency)
{
	bool bReturn = TRUE;

	// We build URL
	String sURLAux = String("/rest/integration/authenticate/RTG/") + sToken;
	wstring sURL = sURLAux;

	Log::msgInformation("Authenticate Call to Int-API: %S", sURL.c_str());

	std::wstring sResult;
	if (!wsAuthenticate->Call(sResult, sURL))
	{
		Log::msgError("Web service error when calling Authenticate. Token=%S", (const uint16*)sToken);
		return FALSE;
	}

	Log::msgInformation("Authenticate Response from Int-API: %S (Token: %S)", sResult.c_str(), (const uint16*)sToken);
	
	// Before parsing we check if it is an html document or empty. If it is html, it is an error (we are asking for a json response)
	String errorMsg;
	if (sResult.empty() || ((sResult.find(TEXT("<html>"))!=string::npos) && (sResult.find(TEXT("</html>"))!=string::npos)))
	{
		bReturn = FALSE;
	}
	else // No error, parse result
	{
		try
		{
			// Now we have the response, parse it. We need:
			JSONNODE* jsonResponse = libJSON::Parse(sResult);
			if (jsonResponse)
			{
				JSONNODE* jsonNodeAux = NULL;
				jsonNodeAux = jsonResponse->GetNode_NoCase(TEXT("int:externalId"));

				// 1: Response code
				if (jsonNodeAux)
				{
					sPID = jsonNodeAux->NodeAsString().c_str();

					// CSN-8909: Look for clientType					
					JSONNODE* jsonProperties = jsonResponse->GetNode(TEXT("int:properties"));
					if (jsonProperties)
						jsonProperties = jsonProperties->GetNode(TEXT("int:property"));
					if (jsonProperties)
					{
						int32 iNodes = jsonProperties->NodeSize();	
						bool bFoundClientType = false;
						bool bFoundCurrency = false;
						for(int i=0; (i < iNodes) && (!bFoundClientType || !bFoundCurrency); ++i)
						{
							jsonNodeAux = jsonProperties->NodeAt(i);
							
							// Get property key and value, if they exist
							std::wstring key = TEXT("");
							std::wstring value = TEXT("");
							
							JSONNODE* jsonNodeKey = jsonNodeAux->GetNode(TEXT("@key"));
							if (jsonNodeKey)
							{
								key = jsonNodeKey->NodeAsString();
															
								if (key == TEXT("clientType"))
								{
									JSONNODE* jsonNodeValue = jsonNodeAux->GetNode(TEXT("@value"));
									if (jsonNodeValue)
										value = jsonNodeValue->NodeAsString();
									
									sClientType = value.c_str();
									bFoundClientType = true;
								}
								else if (key == TEXT("currency"))
								{
									// CSN-11204: Look for currency
									JSONNODE* jsonNodeValue = jsonNodeAux->GetNode(TEXT("@value"));
									if (jsonNodeValue)
										value = jsonNodeValue->NodeAsString();
									
									sCurrency = value.c_str();
									bFoundCurrency = true;
								}
							}
						}	
					}
				}
				else
					bReturn = FALSE;
			}
			else
			{
				bReturn = FALSE;
			}

			// CSN-7802
			if (jsonResponse)
			{
				delete jsonResponse;
				jsonResponse = NULL;
			}

		}
		catch(...)
		{
			bReturn = FALSE;
		}
	}

	if (!bReturn)
	{
		// HTML response: error
		Log::msgError("Web service error when calling Authenticate. Token=%S, Result=%S", 
						(const uint16*)sToken, sResult.c_str());
	}
	
	return bReturn;
}

String
WebServiceCall::base64_encode(String sToEncode)
{
	unsigned char *aucPlainText=NULL;
	int32 iLen = sToEncode.getLength();
	
	aucPlainText=new unsigned char [iLen+1];
	memset(aucPlainText,0,iLen);

	//Convert string to 8
	StringConversions::convert((char*)aucPlainText,sToEncode.getBuffer(0),sToEncode.getLength());
	sToEncode.releaseBuffer();
	aucPlainText[sToEncode.getLength()]='\0';
	
   	//Bae64 Encode
	return base64_encode(aucPlainText,iLen);
}

// CSN-8893 - Credit request to external Wallet
bool WebServiceCall::CreditWallet(const WalletTransaction *transaction, int32 *balance)
{
	if (!wsCreditWallet)
		return FALSE;

	bool bReturn = TRUE;

	// Set URL
	wstring sURL = String("/rest/integration/product/") + 
		           ServerOpts::intAPIProduct + String("/player/") + 
				   transaction->pid + String("/transfer");

	// Set POST data
	String sPostData;
	sPostData.sprintf(TEXT(POSTDATA_CREDITWALLET),
		              (const wchar_t*)transaction->pid,
					  (const wchar_t*)transaction->currency,
					  transaction->amount,
					  transaction->roundId,
					  transaction->sessionId,
					  transaction->gameId,
					  transaction->machId,
					  transaction->roundId,
					  (const wchar_t*)transaction->transactionId);
	int32 len = sPostData.getLength();
	char* sPostData2 = (char*)_alloca(len + 1);
	StringConversions::convert(sPostData2, sPostData, len + 1);

	// Perform the call
	std::wstring sResult;
	if (!wsCreditWallet->Call(sResult, sURL, sPostData2))
	{
		Log::msgError("Web service error when calling CreditWallet. pid=%S", (const uint16*)transaction->pid);
		return FALSE;
	}
	
	// Check that we haven't received an HTML response
	String errorMsg;
	if (sResult.empty() || ((sResult.find(TEXT("<html>"))!=string::npos) && (sResult.find(TEXT("</html>"))!=string::npos)))
		bReturn = FALSE;
	else
	{
		// TODO: Parse new balance from the XML response
		DEBUGMSG(("TODO: Parse new balance from the XML response"));
	}

	// TODO: Remove this and keep the balance retrieved from wsCreditWallet call
	if (bReturn)
		GetBalanceWallet(transaction->pid, balance);

	if (!bReturn)
		Log::msgError("Web service error when calling CreditWallet. pid=%S", (const uint16*)transaction->pid);
	
	return bReturn;
}

// CSN-8893 - Credit request to external Wallet
bool WebServiceCall::DebitWallet(const WalletTransaction *transaction, int32 *balance)
{
	if (!wsDebitWallet)
		return FALSE;

	bool bReturn = TRUE;

	// Set URL
	wstring sURL = String("/rest/integration/product/") + 
		           ServerOpts::intAPIProduct + String("/player/") + 
				   transaction->pid + String("/transfer");

	// Set POST data
	String sPostData;
	sPostData.sprintf(TEXT(POSTDATA_DEBITWALLET),
		              (const wchar_t*)transaction->pid,
					  (const wchar_t*)transaction->currency,
					  transaction->amount,
					  transaction->roundId,
					  transaction->sessionId,
					  transaction->gameId,
					  transaction->machId,
					  transaction->roundId,
					  (const wchar_t*)transaction->transactionId);
	int32 len = sPostData.getLength();
	char* sPostData2 = (char*)_alloca(len + 1);
	StringConversions::convert(sPostData2, sPostData, len + 1);

	// Perform the call
	std::wstring sResult;
	if (!wsDebitWallet->Call(sResult, sURL, sPostData2))
	{
		Log::msgError("Web service error when calling DebitWallet. pid=%S", (const uint16*)transaction->pid);
		return FALSE;
	}
	
	// Check that we haven't received an HTML response
	String errorMsg;
	if (sResult.empty() || ((sResult.find(TEXT("<html>"))!=string::npos) && (sResult.find(TEXT("</html>"))!=string::npos)))
		bReturn = FALSE;
	else
	{
		// TODO: Parse new balance from the XML response
		DEBUGMSG(("TODO: Parse new balance from the XML response"));
	}

	// TODO: Remove this and keep the balance retrieved from wsDebitWallet call
	if (bReturn)
		GetBalanceWallet(transaction->pid, balance);

	if (!bReturn)
		Log::msgError("Web service error when calling DebitWallet. pid=%S", (const uint16*)transaction->pid);
	
	return bReturn;
}

// CSN-8893 - Balance request to external Wallet
bool WebServiceCall::GetBalanceWallet(String pid, int32 *balance)
{
	if (!wsGetBalanceWallet)
		return FALSE;

	// TODO: Perform the web service call
	*balance = 1000;
	
	return TRUE;
}

String 
WebServiceCall::base64_encode(unsigned char const* bytes_to_encode, int32 len)
{
	String ret = String::emptyString;
	int i = 0;
	int j = 0;
	unsigned char char_array_3[3];
	unsigned char char_array_4[4];

	while (len--) {
		char_array_3[i++] = *(bytes_to_encode++);
		if (i == 3) {
			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;

			for(i = 0; (i <4) ; i++)
				ret += String(base64_chars[char_array_4[i]]);
			i = 0;
		}
	}

	if (i)
	{
		for(j = i; j < 3; j++)
			char_array_3[j] = '\0';

		char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
		char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
		char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
		char_array_4[3] = char_array_3[2] & 0x3f;

		for (j = 0; (j < i + 1); j++)
			ret += String(base64_chars[char_array_4[j]]);

		while((i++ < 3))
			ret += String('=');

	}

	return ret;
}

/*
std::string
CApp::base64_decode( std::string const& encoded_string) {
  int in_len = encoded_string.size();
  int i = 0;
  int j = 0;
  int in_ = 0;
  unsigned char char_array_4[4], char_array_3[3];
  std::string ret;

  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
    char_array_4[i++] = encoded_string[in_]; in_++;
    if (i ==4) {
      for (i = 0; i <4; i++)
        char_array_4[i] = base64_chars.find(char_array_4[i]);

      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

      for (i = 0; (i < 3); i++)
        ret += char_array_3[i];
      i = 0;
    }
  }

  if (i) {
    for (j = i; j <4; j++)
      char_array_4[j] = 0;

    for (j = 0; j <4; j++)
      char_array_4[j] = base64_chars.find(char_array_4[j]);

    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
  }

  return ret;
}
*/




